Skip to content

feat(calendar): 公版日历与日程模块(关联 #48、#95) - #113

Open
Zewang0217 wants to merge 8 commits into
ECNU:mainfrom
Zewang0217:feat/calendar
Open

Zewang0217 wants to merge 8 commits into
ECNU:mainfrom
Zewang0217:feat/calendar

Conversation

@Zewang0217

@Zewang0217 Zewang0217 commented Oct 8, 2026 •

Copy link
Copy Markdown

问题与修改

关联 #48,以及路线图 #95(EduWork Roadmap — 2026 Q4)里的「日历与日程 · 通用日历模块」。

本 PR 加入公版的日历与日程模块:一份提案文档(docs/proposals/calendar/)与插件 dsh-plugins/calendar(@eduwork/dsh-calendar)——周视图、本机存储、ICS 导入导出、新建与编辑日程的完整交互,并把同一份日历以工具的形式开放给会话里的模型读写。

公版只有日程(event)这一种记录,不含任何学校专属字段。学校课表、校历与教务规则由机构插件作为数据来源注入:一门课的一次课就是一条日程,按 source 归属与着色、按 recurrence 每周重复;同一个结构也装得下邮件、活动与个人安排。

面板与交互:

  • 侧栏入口与主区面板复用产品既有的两个插槽。一周七列共用一条小时轴,今天列高亮,当前时刻画一条线,同一时段重叠的日程并排分列。
  • 不带时间的全天记录画在它覆盖的每一天的日期表头里,不占时间轴,跨天的一条在每一天都出现。
  • 新建有四种入口:顶部按钮、空日历的引导卡、双击空白时段(预填 1 小时)、在空白时段按住拖动(松手后弹窗预填该段)。
  • 点任意一条打开详情:改期或改地点、撤销改期、取消这一次(exceptions)、恢复、删除整条;只改开始时间时结束时间跟着走,条目保持原时长。
  • 日程页左栏改标题、日期、起止时间、地点与备注,右栏按月份列出最近一年的出现时间。
  • 导入是弹窗,支持把 .ics 拖到面板、在弹窗里选择文件或粘贴文本,按来源作用域替换;导出可复制或下载。

周视图:一周七列共用一条小时轴,重叠的两条并排分列;不带时间的记录画在日期表头(10/6–10/8 与 10/9)

对模型的读写接口:

  • 五个工具:calendar_list_events 读一个区间;calendar_create_event、calendar_update_event、calendar_delete_event 与 calendar_occurrence(改期、复位、取消、恢复系列中的某一次)写。
  • 区间一律有界:默认往前 180 天、往后 366 天,单次上限 3660 天。
  • 写入只带要改的字段,留空即保持原值;挪动开始时间时保留原时长。
  • 写入在 tools/pre-execute 上统一判定:读取放行、写入询问、「无审批」会话放行、没有会话上下文的写入拒绝。

模型调用 calendar_create_event 后写入停在待确认状态,由用户决定拒绝或允许一次

数据与存储:

  • 记录字段沿用 ICS 词汇(uid/title/start/end/location/description/recurrence/exceptions/overrides/source/extensions),未识别字段原样保留在 extensions。
  • 存放于本机存储域的一张 events 表(按 uid):写入先落盘再可见,读取取自内存状态,变更以 domain/changed 通知;不联网、不上传、不写日志。
  • 一批记录按 source 归属:同一来源再次导入整体替换,其他来源与手动记录不受影响。
  • 发生(occurrence)展开时,结束时间按记录自身跨越的天数平移,多天的全天记录与跨午夜的记录都不会退化成当天结束或结束早于开始。
  • ICS 是自研的最小实现(VEVENT、RRULE 子集、EXDATE、RECURRENCE-ID,行折叠、转义与 UTF-8 按 RFC 5545),不引入第三方日历解析依赖。

验证

  • node --test dsh-plugins/calendar/test/*.test.mjs:81 例通过(记录契约 8、发生展开 16、ICS 18、远端契约 5、存储 9、布局几何 16、工具 9)。EDUWORK_TEST_RUNTIME 分别指向 release-v0.1.5-rc.2 与 candidate-v0.2.0-rc.2,两条固定运行时线各 81 例通过,无跳过、无失败。
  • scripts/check-source-docs.mjs 通过:{ "files": 277, "links": 799, "readmePairs": 41, "broken": [], "readmeIssues": [] }。lib/client.js 与按 dsh-plugins/calendar/build-client.ps1 重新构建的结果逐字节一致。
  • scripts/ci-eduwork-web.ps1 全量 Web 装配通过,插件正常进入装配与 composition.json(bundles 3 / plugins 6 / schemaFields 28),eduwork-calendar 与 eduwork-calendar-tools 都在清单里。
  • 装配后的产品里实测:周视图的今天高亮、当前时刻线、重叠并排分列;全天记录在覆盖的每一天都出现且可改;四种新建入口;详情弹窗的改期、撤销改期、取消这一次、恢复与删除;日程页左栏编辑与右栏按月列出;每周重复的 exceptions 与 overrides;导入的选择文件、粘贴与拖拽三种入口及同来源替换;导出内容含 RRULE、EXDATE、RECURRENCE-ID 与 DTSTART;VALUE=DATE 且复制可用;空日历引导卡片。
  • 模型侧在装配后的产品里用真实会话执行:读取区间并据此汇总本周安排;新建、修改、按单次取消与改期、删除各一次,改动都落到存储;写入确认点「拒绝」后确认存储无变化。
  • 另用带 courseId 与 courses 表的旧存储验证:应用能正常打开,盘上文档被改写成只剩 events。
  • 未验证范围:未构建桌面安装包,未做 Electron 侧验收;未接真实教务系统或校历订阅源,只用自造的 ICS 文本;界面文案为中文,README_EN.md 同步了功能描述但界面未做本地化;桌面端与机构版行为不在本 PR 范围。两处截图中的数据是合成数据。

可复核的命令:

# EDUWORK_TEST_RUNTIME 指向任一固定运行时线:
node --test dsh-plugins/calendar/test/*.test.mjs
node scripts/check-source-docs.mjs
# 需要本地装配时(-Output 指向一个尚不存在的目录):
scripts/ci-eduwork-web.ps1 -CoreRoot <repo> -Output <new-directory> -RuntimeSource <runtime> -BuildOnly

兼容性与后续事项

  • 不改动现有插件的公开导出、配置标识与数据路径;不新增运行时依赖、不改依赖锁、不新增需单独发布的 npm 包。
  • 新增 @eduwork/dsh-calendar/tools 出口,以及 config/distributions/generic.json 里一条 insert 注册(与 search-auto 同一种形式);发行清单既有插件的顺序与配置不变。
  • 动了 .github/workflows/validate-pinned-dsh-source.yml 两行:把插件的单元测试挂进这条既有 CI 作业(它本来就逐个插件列出测试,路径过滤含 dsh-plugins/**)。位置是否合适请一并确认。
  • 存储域版本仍是 1,记录快照 schemaVersion 仍是 2。记录契约曾带 courseId:旧介质按「公开契约 + 已退休字段」的 schema 读取,开库时改写成当前形状一次,无其他迁移。
  • 机构版注入入口与提醒能力留待后续开发:机构版按 core.lock.json 钉住的公版版本接入,适配器放 edition/plugins/,本 PR 不含 edition/ 的任何改动。

需要重点评审

  • 日程记录契约(lib/model.js 的字段、schemaVersion、source 与 extensions 的语义):这是本 PR 新增的唯一公开数据契约,机构版的数据注入与后续同步都会建立在它上面,字段的最终形态请在这里定。
  • 客户端半与 Host 之间的方法面(lib/typert-schemas.js、lib/typert-descriptors.js,共 10 个远端方法),以及五个工具的名字、参数与结果 schema(lib/tools.js)。
  • 两个写入工具的职责分工:calendar_update_event 改整条记录(含重复记录的每一次),calendar_occurrence 只改系列中的某一次。提案里记了取舍,若不认可可以合成为单个工具。
  • 写入判定的最后一档:没有会话上下文的写入直接拒绝,是否会挡住将来的脚本化调用。

@Zewang0217 Zewang0217 changed the title feat(calendar): 日历与日程模块提案 + 面板骨架(关联 #48、#95) feat(calendar): 公版日历与日程模块(关联 #48、#95) Oct 10, 2026
@Zewang0217
Zewang0217 force-pushed the feat/calendar branch 2 times, most recently from 211cc90 to e4ff8dc Compare October 10, 2026 14:31
提案文档:目标、插件身份、范围、日程数据结构、为什么公版里没有课程实体、重复规则
与 ICS 边界、展示层与 Agent 读写边界、数据形态与存储、提醒与机构版边界、兼容性
边界、开发拆分与待明确项。中英文互为语言切换,并附两张界面截图。

Refs ECNU#48.
记录只有日程这一种:字段沿用 ICS 词汇(uid、title、start、end、location、
description、recurrence、exceptions、overrides、source、extensions),未识别字段
原样保留在 extensions;一批记录按 source 归属,同一来源的再次导入整体替换,其他
来源与手动记录不受影响。

存储域里一张 events 表,按 uid 存取:写入先落盘再可见,读取取自内存状态,变更以
domain/changed 通知,不联网、不上传、不写日志。记录契约曾带 courseId,旧存储仍能
打开,开库时改写成当前形状一次。

展开发生(occurrence)时按记录自身跨越的天数平移结束时间,多天的整日记录与跨午夜
的记录都不会退化成当天结束或结束早于开始。

ICS 是自研的最小实现(VEVENT、RRULE 子集、EXDATE、RECURRENCE-ID,行折叠、转义与
UTF-8 按 RFC 5545),不引入第三方日历解析依赖。

Host 半以 calendar 服务暴露上述读写,客户端半通过同一份 typert 描述符调用其中的
10 个方法。

Refs ECNU#48.
侧栏入口与主区面板复用产品既有的两个插槽。一周七列共用一条小时轴,今天列高亮,
当前时刻画一条线,同一时段重叠的日程并排分列;不带时间的全天记录画在它覆盖的每
一天的日期表头里,不占时间轴,跨天的一条在每一天都出现;打开时滚到这周有日程的
时段。

新建有四种入口:顶部「新建日程」、空日历的引导卡、双击空白时段(预填 1 小时),
以及在空白时段按住拖动(拖动中画出这一段的虚线框,松手后弹窗预填该段)。

点任意一条打开详情:改期或改地点、撤销改期、取消这一次、恢复、删除整条;只改开始
时间时结束时间跟着移动、条目保持原时长。日程页左栏改标题、日期、开始与结束时间、
地点与备注,右栏按月份列出最近一年的出现时间。

导入是弹窗,支持把 .ics 拖到面板任意位置、在弹窗里选择文件或粘贴文本,按来源作用域
替换;导出可复制或下载。颜色跟着来源,同一来源同一色调。

取轴、重叠分列几何与改期时的时长保持放在纯函数里单独测试。

Refs ECNU#48.
五个工具覆盖同一份日历:calendar_list_events 读一个区间,calendar_create_event、
calendar_update_event、calendar_delete_event 与 calendar_occurrence 写,最后一个
只处理系列的某一次(改期、复位、取消、恢复)。

区间一律有界:不写 from 就从今天往前 180 天,不写 to 就给一年,单次最多 3660 天。
写入只带要改的字段,留空保持原值,挪动开始时间时保留原时长。

写入在 tools/pre-execute 上统一判定:读取放行、写入询问、已经「无审批」的会话放行、
没有会话上下文的写入拒绝。工具与面板写同一份记录、走同一套校验。

Refs ECNU#48.
发行清单与机构装配各加一条插件登记,客户端构建脚本把 calendar 的 src/client.ts
纳入构建,插件单测挂进「钉住 DSH 候选源」的 CI 作业。

Refs ECNU#48.
@Zewang0217
Zewang0217 marked this pull request as ready for review October 10, 2026 14:54
@Zewang0217

Copy link
Copy Markdown
Author

你好,我已经更新了分支。请问 @freedomkk-qfeng 能否帮忙批准一下工作流,让它跑一下 CI 检查?谢谢!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant